

#include "as_socket.h"
#include "byte_buffer.h"
#include "net_addr.h"
#include <assert.h>
#include <iostream>

#ifdef _WIN32
#include <mstcpip.h>
#else
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

namespace as
{
namespace inet
{

AsSocket::AsSocket()
{
#ifdef _WIN32
    m_fd = WSASocketW(AF_INET, SOCK_STREAM, 0, nullptr, 0, WSA_FLAG_OVERLAPPED);
#else
    m_fd = ::socket(AF_INET, SOCK_STREAM, 0);
#endif
    assert(m_fd != SOCKET_ERR_FD);
}

AsSocket::AsSocket(socket_fd fd)
    :m_fd(fd)
{

}


AsSocket::~AsSocket()
{

}

int AsSocket::bind(NetAddr &netDetail)
{
    auto &addr = netDetail.getIpv4();
    int rc = ::bind(this->m_fd, (struct sockaddr *)&addr, sizeof(addr));
    if(rc == SOCKET_ERR_FD)
    {
        rc = -1;
        goto FAIL;
    }

    return 0;

FAIL:
    close(m_fd);
    return rc;
}

int AsSocket::connect(NetAddr &netDetail)
{
    auto &addr = netDetail.getIpv4();
    while(1)
    {
        int rc = ::connect(this->m_fd, (struct sockaddr *)&addr, sizeof(addr));
        if(rc == SOCKET_ERR_FD)
        {
            int err = AsSocket::get_last_err();
#ifdef _WIN32
            if(err != WSA_IO_PENDING && err != WSAEWOULDBLOCK && err != WSAEISCONN && err != WSAECONNREFUSED)
            {
                std::cout << "connect err:" << err << std::endl;
                close(m_fd);
                return rc;
            }
            else
            {
                std::cout << "wait for server login" << std::endl;
                Sleep(1000);
                continue;
            }
#else

#endif
        }

        break;
    }

    return 0;
}

int AsSocket::listen()
{
    int rc = ::listen(m_fd, SOMAXCONN);
    if(rc == SOCKET_ERR_FD)
    {
        close(m_fd);
        return -1;
    }
    return 0;
}

socket_fd AsSocket::accept()
{
    sockaddr_in addr;
    socklen_t len = sizeof(addr);
    socket_fd fd = ::accept(m_fd, (struct sockaddr *)&addr, &len);
    if(fd == SOCKET_ERR_FD)
    {
        close(m_fd);
        m_fd = SOCKET_ERR_FD;
        return -1;
    }

    return fd;
}

int AsSocket::start_server(NetAddr &addr)
{
    int rc = bind(addr);
    if(rc == SOCKET_ERR_FD)
    {
        return -1;
    }

    return 0;
}

int AsSocket::recv(socket_fd fd, void *buf, int len, int flags)
{
    int rc = ::recv(fd, (char*)buf, len, flags);
    return rc;
}


void AsSocket::close(socket_fd fd)
{
#ifdef _WIN32
    ::closesocket(fd);
#else
    ::close(fd);
#endif
}

void AsSocket::close()
{
    AsSocket::close(m_fd);
    m_fd = SOCKET_ERR_FD;
}

int AsSocket::recv(ByteBufferPtr &buf)
{
    int curPos = 0;
    bool loot = true;
    buf->make_writeable_bytes(2048);
    int rc = 0;
    do
    {
        rc = recv(m_fd, (void*)buf->GetWritePtr(), buf->WriteableBytes());
        if(rc > 0)
        {
            curPos += rc;
            buf->SetWritePos(buf->GetWritePos() + rc);
            std::cout << "read:" << rc << std::endl;
            loot = false;
        }
        else if(rc == 0)
        {
            std::cout << "read:0" << std::endl;
            loot = false;
        }
        else
        {
            loot = true;
        }


    } while (loot);
    return rc;
}

void AsSocket::set_non_block(socket_fd fd)
{
#ifdef _WIN32    
    unsigned long set = 1;
    ioctlsocket(fd, FIONBIO, &set);
#else
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#endif
}

void AsSocket::set_non_block()
{
    AsSocket::set_non_block(m_fd);
}

void AsSocket::set_keep_alive(socket_fd fd)
{
#ifdef _WIN32
	tcp_keepalive alive_in;
	tcp_keepalive alive_out;
	alive_in.onoff = TRUE;
	alive_in.keepalivetime = 1000 * 60;
	alive_in.keepaliveinterval = 1000 * 10;
	unsigned long ulBytesReturn = 0;
	if (SOCKET_ERROR == WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in), &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL))
	{
        //LOG_ERROR << "set keep alive error" << WSAGetLastError() << std::endl;
	}
#else
    int opt = 1;
    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
#endif
}

void AsSocket::set_keep_alive()
{
    AsSocket::set_keep_alive(m_fd);
}

int AsSocket::get_last_err()
{
#ifdef _WIN32
    return WSAGetLastError();
#else
    return errno;
#endif
}
}
}
